"""
Symbol resolver for namespace and qualified name resolution.

This module provides the SymbolResolver class which handles:
- Namespace mapping (C# -> C++ nested namespaces)
- Qualified name resolution
- Using directive handling
- Symbol lookup and resolution
"""

from __future__ import annotations

import re


class SymbolResolver:
    """
    Resolves C# symbols to C++ equivalents.

    Handles namespace mappings, qualified names, and using directives.
    """

    def __init__(self) -> None:
        """Initialize the SymbolResolver"""
        self.namespace_mappings: dict[str, str] = {}
        self.using_directives: list[str] = []
        self.using_aliases: dict[str, str] = {}

    def add_namespace_mapping(self, csharp_namespace: str, cpp_namespace: str) -> None:
        """
        Add a custom namespace mapping.

        Args:
            csharp_namespace: C# namespace (e.g., "System.Collections.Generic")
            cpp_namespace: C++ namespace (e.g., "std")
        """
        self.namespace_mappings[csharp_namespace] = cpp_namespace

    def add_using_directive(self, namespace: str) -> None:
        """
        Add a using directive (for future context-aware resolution).

        Args:
            namespace: Namespace being imported (e.g., "System.Collections.Generic")
        """
        if namespace not in self.using_directives:
            self.using_directives.append(namespace)

    def add_using_alias(self, alias: str, target: str) -> None:
        """
        Add a using alias (e.g., "using StringList = List<string>").

        Args:
            alias: Alias name
            target: Target type or namespace
        """
        self.using_aliases[alias] = target

    def resolve_namespace(self, csharp_namespace: str) -> str:
        """
        Resolve a C# namespace to its C++ equivalent.

        Args:
            csharp_namespace: C# namespace (e.g., "MyApp.Services")

        Returns:
            C++ namespace (e.g., "my_app::services")

        Examples:
            >>> resolver.resolve_namespace("MyApp.Services")
            "my_app::services"

            >>> resolver.resolve_namespace("System.Collections.Generic")
            "std"  # if mapped
        """
        # Check for exact mapping
        if csharp_namespace in self.namespace_mappings:
            return self.namespace_mappings[csharp_namespace]

        # Check for prefix mapping
        for cs_ns, cpp_ns in self.namespace_mappings.items():
            if csharp_namespace.startswith(cs_ns + "."):
                # Replace the prefix
                remainder = csharp_namespace[len(cs_ns) + 1 :]
                cpp_remainder = self._convert_namespace_part(remainder)
                return f"{cpp_ns}::{cpp_remainder}"

        # Default conversion: convert to snake_case and use :: separator
        return self._convert_namespace_part(csharp_namespace)

    def _convert_namespace_part(self, namespace: str) -> str:
        """
        Convert a C# namespace to C++ style.

        Args:
            namespace: C# namespace (e.g., "MyApp.Services")

        Returns:
            C++ namespace (e.g., "my_app::services")
        """
        # Split by dots
        parts = namespace.split(".")

        # Convert each part to snake_case
        cpp_parts = [self._to_snake_case(part) for part in parts]

        # Join with ::
        return "::".join(cpp_parts)

    def _to_snake_case(self, name: str) -> str:
        """
        Convert PascalCase or camelCase to snake_case.

        Args:
            name: Name in PascalCase or camelCase

        Returns:
            Name in snake_case

        Examples:
            >>> _to_snake_case("MyClassName")
            "my_class_name"

            >>> _to_snake_case("HTTPServer")
            "http_server"
        """
        # Handle acronyms (e.g., HTTPServer -> http_server)
        # Insert underscore before uppercase letters that follow lowercase letters
        s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)

        # Insert underscore before uppercase letters that are followed by lowercase letters
        # and preceded by uppercase letters (for acronyms)
        s2 = re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1)

        return s2.lower()

    def resolve_qualified_name(self, csharp_name: str) -> str:
        """
        Resolve a fully qualified C# name to C++ style.

        Args:
            csharp_name: Qualified name (e.g., "MyApp.Services.UserService")

        Returns:
            C++ qualified name (e.g., "my_app::services::user_service")

        Examples:
            >>> resolver.resolve_qualified_name("MyApp.Services.UserService")
            "my_app::services::user_service"
        """
        # Check if it's an alias
        if csharp_name in self.using_aliases:
            csharp_name = self.using_aliases[csharp_name]

        # Split into parts
        parts = csharp_name.split(".")

        if len(parts) == 1:
            # No namespace, just a type name
            return self._to_snake_case(parts[0])

        # The last part is the type name, the rest is namespace
        namespace_parts = parts[:-1]
        type_name = parts[-1]

        namespace = ".".join(namespace_parts)
        cpp_namespace = self.resolve_namespace(namespace)
        cpp_type = self._to_snake_case(type_name)

        return f"{cpp_namespace}::{cpp_type}"

    def get_simple_name(self, qualified_name: str) -> str:
        """
        Extract the simple name from a qualified name.

        Args:
            qualified_name: Qualified name (e.g., "System.String")

        Returns:
            Simple name (e.g., "String")
        """
        return qualified_name.split(".")[-1]

    def is_system_namespace(self, namespace: str) -> bool:
        """
        Check if a namespace is a system namespace.

        Args:
            namespace: Namespace to check

        Returns:
            True if it's a system namespace (starts with "System")
        """
        return namespace.startswith("System") or namespace.startswith("Microsoft")

    def setup_default_mappings(self) -> None:
        """Setup default C# to C++ namespace mappings"""
        # System namespaces
        self.add_namespace_mapping("System", "std")
        self.add_namespace_mapping("System.Collections.Generic", "std")
        self.add_namespace_mapping("System.Collections", "std")
        self.add_namespace_mapping("System.IO", "std::filesystem")
        self.add_namespace_mapping("System.Text", "std")
        self.add_namespace_mapping("System.Linq", "std::ranges")

    def get_cpp_namespace_parts(self, cpp_namespace: str) -> list[str]:
        """
        Split a C++ namespace into parts.

        Args:
            cpp_namespace: C++ namespace (e.g., "my_app::services::core")

        Returns:
            List of namespace parts (e.g., ["my_app", "services", "core"])
        """
        if not cpp_namespace:
            return []
        return cpp_namespace.split("::")
